﻿using System;
using System.Linq.Expressions;
using MySql.Simple.Mapper.Finders;
using MySql.Simple.Mapper.Linq.Query;

namespace MySql.Simple.Mapper.Linq.ExpressionVisitors
{
    /// <summary>
    /// Transforms <see cref="NewExpression"/> objects into MySQL syntax
    /// </summary>
    public class ConstantExpressionVisitor : IExpressionVisitor
    {
        /// <summary>
        /// Determines whether this instance can visit the specified expression.
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <returns>
        /// 	<c>true</c> if this instance can visit the specified expression; otherwise, <c>false</c>.
        /// </returns>
        public bool CanVisit(Expression expression)
        {
            return expression is ConstantExpression;
        }

        /// <summary>
        /// Visits the specified expression and returns a string representation of it's contents.
        /// </summary>
        /// <param name="expression">The expression.</param>
        /// <param name="query">The query.</param>
        /// <returns></returns>
        public SqlQuery Visit(Expression expression, SqlQuery query)
        {
            var constantExpression = (ConstantExpression)expression;

            if (IsSubclassOfRawGeneric(typeof(Query<>), constantExpression.Value.GetType()))
            {
                var type = GetSubclassOfRawGeneric(constantExpression.Value.GetType());

                query.Table = "`" + TableNameFinder.Find(type) + "`";
            }
            else
            {
                query.Fields += constantExpression.Value.ToMySqlSyntax();                
            }

            return query;
        }

        static bool IsSubclassOfRawGeneric(Type generic, Type toCheck)
        {
            while (toCheck != typeof(object))
            {
                var cur = toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck;

                if (generic == cur)
                {
                    return true;
                }

                toCheck = toCheck.BaseType;
            }

            return false;
        }

        static Type GetSubclassOfRawGeneric(Type toCheck)
        {
            var result = toCheck;

            while (toCheck != typeof(object))
            {
                if (toCheck.IsGenericType)
                {
                    foreach (Type arg in toCheck.GetGenericArguments())
                    {
                        result = arg;
                    }
                }

                toCheck = toCheck.BaseType;
            }

            return result;
        }

    }
}
